import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.text.DecimalFormat;
import java.util.Hashtable;


import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;





/**
 * Die Klasse GUI ist eine Unterklasse von JFrame und erzeugt wenn Sie
 * Konstruiert wird ein solches mit vorher spezifiziertem Inhalt.
 * 
 * @author Fabian
 * 
 */
public class GUI extends JFrame implements ActionListener {
	public static final int EINHEIT_MIN = 0;
	public static final int EINHEIT_INIT = 50;
	public static final int EINHEIT_MAX = 100;
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	
	
	/*
	 * Die hier definierten Textfelder, Buttons, Icons, etc muessen als Klassenvaribalen deklariert sein, damit auch der ActionLisener darauf zugreifen kann und die Elemente asuwerten oder veraendern darf
	 * 
	 *  -- Eine alternative waere evtl. getter- und setter-Methoden zu verwenden
	 *  -- Anm.: Scheint mir nicht viel "schoener" als jetzige Variante
	 *  
	 *  TODO: evtl. alle Buttons und Textfelder hier oben einheitlich deklarieren ?!
	 */
	private ImageIcon markOff = new ImageIcon("src/images/markierungOff.png");
	private ImageIcon markOn = new ImageIcon("src/images/markierungOn.png");
	private JButton setMarkierungBt = new JButton("Markierung ", markOff);
	private JButton setDiffBt = new JButton("Differenz");
	private JTextField eingKFunktion = new JTextField("Kostenfunktion", 30);
	private JTextField eingEFunktion = new JTextField("Erloesfunktion", 30);
	
	private JTextField setIntervallVonTF = new JTextField("von", 3);
	private JTextField setIntervallBisTF = new JTextField("bis", 3);
	
	private JTextField getYValueOfXValueTF = new JTextField("x", 3);
	private JTextField getXValueOfYValueTF = new JTextField("y", 3);
	
	
	private JSlider ySkala = new JSlider(JSlider.VERTICAL,EINHEIT_MIN,EINHEIT_MAX,EINHEIT_INIT);
	private JSlider xSkala = new JSlider(JSlider.HORIZONTAL,EINHEIT_MIN,EINHEIT_MAX, EINHEIT_INIT);
	
	private PaintingComponent drawingArea;
	
	/**
	 * Die Main Methode legt ein neues Fenster an bestimmt seine Startgroesse und initialisiert das erste zeichnen.
	 * Danach allerdings uebernehmen die Klassen und Methoden, die von dem Benutzer ueber die Benutzungsoberfl�che aufgerufen werden s�mtliche Aufgaben.
	 * 
	 * @param argv
	 */
	public static void main(String argv[]) {
		
		
		GUI myGui = new GUI();
		myGui.setLocation(100, 100);
		myGui.setSize(1000, 800);
		myGui.setVisible(true);
		myGui.repaint();
	}

	/**
	 * Der Konstruktor einer GUI
	 * 
	 * an dieser Stelle sollte man sich erneut klarmachen, dass GUI ein JFRAME ist!
	 * mit super("...") wird also ein neues Fenster mit einem Namen initialisiert.
	 * Das GUI Fenster bestimmt im Konstruktor das naehere aussehen dieses Fensters und platziert Elemente etc auf der Oberfl�che.
	 */
	public GUI() {
		super("Analysetool");
		//Ein paar Hauptmerkmale wie das HauptLayout werden festgelegt.
		//Wir haben uns fuer das GridBagLayout, als aeusserst flexible und maechtige Layoutverwaltung entschieden
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		GridBagLayout mainLayout = new GridBagLayout();
		GridBagConstraints constraints = new GridBagConstraints();
		this.setLayout(mainLayout);
		//Eine 'drawingArea' vom Typ PaintingComponent wird erstellt und bekommt einem MouseListener, der auf Klicks und Events der Maus inenrhalb der Area achtet.
		//Die drawing Area ist im Allgemeinen fuer das Zeichnen zustaendig -- Details siehe Klasse Painting Component
		drawingArea = new PaintingComponent();
		drawingArea.addMouseListener(new ListenerMouseVerschiebung(drawingArea));
		
		/*
		 * Des weiteren gibt es noch 3 andere Bereiche in das das Hauptfenster aufgeteilt wurde.
		 * ll -> left lower		Panel links unten 
		 * rl -> right lower	Panel rechts unten
		 * ru -> right upper	Panel rechts oben
 		 */
		JPanel llPanel = new JPanel();
		JPanel ruPanel = new JPanel();
		JPanel rlPanel = new JPanel();

		/*
		 * Hier wird das linke untere Panel best�ckt:
		 * 	-- zwei Texteingabefelder zum eingeben der Funktionen
		 * 	-- zwei Buttons zum jeweiligen Absender einer Funktion
		 */
		llPanel.setLayout(new GridLayout(2, 1));
		llPanel.setSize(-1, 200);
		//Zur besseren Verteilung wird der Bereich erneut in 2 Teile aufgeteilt. Einen unteren und einen oberen mit Hilfe eines einfachen Gitter-Layouts
		//Der obere Bereich ist fuer den Button und das Textfeld der Kostenfunktion, das untere dementsprechend der Erloesfunktion
		JPanel lluContent = new JPanel();
		JPanel lllContent = new JPanel();
		
		
		//Kostenfunktion
		lluContent.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 10));
		JButton showKFBt = new JButton("show");
		showKFBt.setActionCommand("showKFBtClicked");
		showKFBt.addActionListener(this);

		eingKFunktion.setActionCommand("showKFBtClicked");
		lluContent.add(eingKFunktion);
		lluContent.add(showKFBt);
		llPanel.add(lluContent);

		//Erloesfunktion
		lllContent.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 10));
		lllContent.setSize(-1, 100);

		JButton showEFBt = new JButton("show");
		showEFBt.setActionCommand("showEFBtClicked");
		showEFBt.addActionListener(this);
		eingEFunktion.setActionCommand("showEFBtClicked");
		lllContent.add(eingEFunktion);
		lllContent.add(showEFBt);
		llPanel.add(lllContent);

		/*
		 * Das rechte obere Panel beinhaltet die meisten wichtigen Einstellungen fuer das Projekt
		 * Dort werden Intervalle eingestellt und verschiedene Funktionen aktiviert bzw. deaktiviert.
		 * 
		 * Es wird wieder ein GridBagLayout benutzt, da viele kleine Elemente an vernuenftige Stellen gesetzt werden muessen.
		 */
		ruPanel.setLayout(new GridBagLayout());
		GridBagConstraints ruConstraints = new GridBagConstraints();
		
		//Label "Interval"
		ruConstraints.anchor = GridBagConstraints.NORTH;
		ruConstraints.insets = new Insets(5, 5, 5, 0);
		ruConstraints.gridx = 0;
		ruConstraints.gridy = 0;
		ruConstraints.gridwidth = 4;
		ruPanel.add(new JLabel("Intervall"), ruConstraints);

		//Textfeld "von" Label " - " Textfeld "bis" Button "set"
		ruConstraints.gridy = 1;
		ruConstraints.gridx = 0;
		ruConstraints.gridwidth = 1;
		ruPanel.add(setIntervallVonTF, ruConstraints);
		ruConstraints.gridx = 1;
		ruPanel.add(new JLabel(" - "), ruConstraints);
		ruConstraints.gridx = 2;
		ruPanel.add(setIntervallBisTF, ruConstraints);
		ruConstraints.gridx = 3;
		JButton setIntervallBt = new JButton("set");
		setIntervallBt.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				try{
					double PixProEin = drawingArea.setIntervall(Double.parseDouble(setIntervallVonTF.getText()), Double.parseDouble(setIntervallBisTF.getText()));
					xSkala.setValue((int)(PixProEin*EINHEIT_MAX / (1+PixProEin)));
				}
				catch(NumberFormatException e1){
					JOptionPane.showMessageDialog(null,"Bitte Geben Sie eine gueltige Zahl ein","Keine Zahl!",JOptionPane.ERROR_MESSAGE);
				}
				catch(IllegalArgumentException e1){
					JOptionPane.showMessageDialog(null, "Sie haben ungueltige Parameter eingegeben!\n"+e1.getMessage(),"Ungueltige Eingabe",JOptionPane.ERROR_MESSAGE);
				}
				
			}
		});
		ruPanel.add(setIntervallBt, ruConstraints);
		
		//Anzeige ob Markierungen an oder aus sind -- und Button zum Aktivieren bzw. Deaktivieren
		setMarkierungBt.setActionCommand("setMarkierungBtDeakt");
		setMarkierungBt.addActionListener(this);
		ruConstraints.insets = new Insets(20, 5, 20, 0);
		ruConstraints.gridy = 2;
		ruConstraints.gridx = 0;
		ruConstraints.gridwidth = 4;
		ruConstraints.anchor = GridBagConstraints.WEST;
		ruPanel.add(setMarkierungBt, ruConstraints);
		
		//Label fuer "Kriege die x und y Werte: "
		ruConstraints.gridy = 3;
		ruConstraints.insets = new Insets(20, 5, 5, 0);
		ruPanel.add(new JLabel("Kriege die X und Y- Werte:"), ruConstraints);	
		
		//Ein- bzw. Ausgabefeld fuer x-Wert
		ruConstraints.gridx = 0;
		ruConstraints.gridy = 4;
		ruConstraints.insets = new Insets(5, 5, 5, 0);
		getYValueOfXValueTF.addFocusListener(new FocusListener() {
			
			@Override
			public void focusLost(FocusEvent e) {
				DecimalFormat df = new DecimalFormat("0.##");
				try{
					getXValueOfYValueTF.setText(""+df.format(drawingArea.getYofAktFunktion(Double.parseDouble(getYValueOfXValueTF.getText()))));
					
				}
				catch(NullPointerException e1){
					JOptionPane.showMessageDialog(null, "Bisher haben Sie keine gueltige Funktion angegeben!","Keine gueltige Funktion",JOptionPane.WARNING_MESSAGE);
				}
			}
			
			@Override
			public void focusGained(FocusEvent e) {
				// TODO Auto-generated method stub
				
			}
		});
		getYValueOfXValueTF.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				DecimalFormat df = new DecimalFormat("0.##");
				try{
					getXValueOfYValueTF.setText(""+df.format(drawingArea.getYofAktFunktion(Double.parseDouble(getYValueOfXValueTF.getText()))));
					
				}
				catch(NullPointerException e1){
					JOptionPane.showMessageDialog(null, "Bisher haben Sie keine gueltige Funktion angegeben!","Keine gueltige Funktion",JOptionPane.WARNING_MESSAGE);
				}
			}
		});
		
		ruPanel.add(getYValueOfXValueTF, ruConstraints);
		
		//Textlabel "<-->"
		ruConstraints.gridx = 1;
		ruConstraints.gridwidth = 2;
		ruConstraints.anchor = GridBagConstraints.CENTER;
		ruPanel.add(new JLabel("<-->"), ruConstraints);
		
		//Ein- bzw. Ausgabefeld fuer y
		ruConstraints.anchor = GridBagConstraints.NORTH;
		ruConstraints.gridwidth = 1;
		ruConstraints.gridx = 3;
		ruPanel.add(getXValueOfYValueTF, ruConstraints);

		
		/*
		 * Der Verscuh die Buttons "Umtauschen" und "Close" einzuarbeiten ... gescheitert bisher
		 * 
		 * TODO: Anderes Layout Probieren, vllt. Boxlayout
		 */
		rlPanel.setLayout(new GridBagLayout());
		GridBagConstraints rlConstraints = new GridBagConstraints();

		rlConstraints.gridx = 0;
		rlConstraints.gridy = 0;
		rlConstraints.insets = new Insets(5, 5, 5, 0);
		rlConstraints.fill = GridBagConstraints.HORIZONTAL;
		rlConstraints.anchor = GridBagConstraints.FIRST_LINE_START;
		JButton umschalteBt = new JButton("Umschalten");
		umschalteBt.setActionCommand("aktFunktionUmschalten");
		umschalteBt.addActionListener(this);
		rlPanel.add(umschalteBt, rlConstraints);

		rlConstraints.gridx=1;
		rlConstraints.gridy=0;
		setDiffBt.setActionCommand("setDff");
		setDiffBt.addActionListener(this);
		rlPanel.add(setDiffBt,rlConstraints);
		
		JButton closeBt = new JButton("close");
		closeBt.setActionCommand("closeBtClicked");
		closeBt.addActionListener(this);
		rlConstraints.gridy = 1;
		rlConstraints.gridx = 1;
		rlConstraints.gridwidth = 2;
		rlConstraints.anchor = GridBagConstraints.PAGE_END;
		rlPanel.add(closeBt, rlConstraints);

		
		/*
		 * Slider (Schieberegler)
		 * 		--Zoomen auf der x bzw. auf der y-Achse
		 * 		--x-Achse Horizontal, y-Achse Vertical angeortnet
		 */
		
		ySkala.addChangeListener(new SliderYChangeListener(drawingArea));
		xSkala.addChangeListener(new SliderXChangeListener(drawingArea));
		ySkala.setMajorTickSpacing(EINHEIT_MAX/10);
		ySkala.setPaintLabels(true);
		ySkala.setPaintTicks(true);
		xSkala.setMajorTickSpacing(EINHEIT_MAX/10);
		xSkala.setPaintLabels(true);
		xSkala.setPaintTicks(true);
		Hashtable<Integer,JLabel> lableTable = new Hashtable<Integer,JLabel>();
		DecimalFormat df = new DecimalFormat("0.##");
		lableTable.put(new Integer(0),new JLabel(""+df.format(1.0/EINHEIT_MAX) ));
		int i=EINHEIT_MIN+10;
		while(i<EINHEIT_MAX){
			lableTable.put(new Integer(i),new JLabel(""+df.format(1.0*i/(EINHEIT_MAX-i))) );
			i=i+10;
		}
		lableTable.put(new Integer(EINHEIT_MAX),new JLabel(""+EINHEIT_MAX) );
		xSkala.setLabelTable(lableTable);
		ySkala.setLabelTable(lableTable);
		this.addMouseWheelListener(new ListenerMouseWheel(xSkala,ySkala));
		
		
		// Die Einstellungen fuer das allgemeine Layout.
		//hier werden die unterschiedlichen Panels im Endeffekt auf das Frame gesetzt
		constraints.gridx = 0;
		constraints.gridy = 0;
		constraints.weightx = 75;
		constraints.weighty = 90;
		constraints.gridheight = 1;
		constraints.anchor = GridBagConstraints.PAGE_START;
		constraints.fill = GridBagConstraints.BOTH;
		constraints.gridwidth = 1;
		this.add(drawingArea, constraints);
		
		constraints.weightx = 0;
		constraints.weighty = 0;
		constraints.gridx = 1;
		this.add(ySkala,constraints);
		
		constraints.weightx = 25;
		constraints.weighty = 90;
		constraints.gridx = 2;
		constraints.fill = GridBagConstraints.NONE;
		this.add(ruPanel, constraints);
		
		constraints.weightx = 0;
		constraints.weighty = 0;
		constraints.gridx = 0;
		constraints.gridy = 1;
		constraints.fill = GridBagConstraints.BOTH;
		constraints.anchor = GridBagConstraints.FIRST_LINE_START;
		this.add(xSkala,constraints);
		
		constraints.weightx = 75;
		constraints.weighty = 10;
		constraints.gridy = 2;
		constraints.fill = GridBagConstraints.NONE;
		this.add(llPanel, constraints);
		
		constraints.weightx = 25;
		constraints.weighty = 10;
		constraints.gridx = 2;
		constraints.fill = GridBagConstraints.BOTH;
		ruConstraints.anchor = GridBagConstraints.LAST_LINE_END;
		this.add(rlPanel, constraints);

		pack();
		//

	}

	/**
	 * Ein MouseListener speziell fuer das Verschieben der Inhalte in einer
	 * PaintingComponent Ein PaintingComponent-Objekt muss mit uebergeben werden,
	 * da auf einige Ihrer Attribute einflussgenommen werden muss.
	 * 
	 * @author Fabian
	 * 
	 */
	static class ListenerMouseVerschiebung extends MouseAdapter {
		int clicked[] = { 0, 0 };
		PaintingComponent drawing;

		public ListenerMouseVerschiebung(PaintingComponent drawingArea) {
			this.drawing = drawingArea;
		}
		/**
		 * Wird die Maus gedrueckt gehalten wird der x- und y- Wert der Klick-Position gespeichert
		 */
		public void mousePressed(MouseEvent e) {
			clicked[0] = (e.getX());
			clicked[1] = (e.getY());
			System.out.println("Cklicked: ( " + clicked[0] + " , " + clicked[1]
					+ " )");
		}
		/**
		 * Beim Loslassen der Maus wird die neue Position der Maus entnommen und die Differenz zur alten Position wird auf den Bezugspunkt des Koordinatensystems hinzuaddiert. Durch ein anschliessendes repaint wird alles neu und relativ zum neuen Bezugspunkt gezeichnet.
		 */
		public void mouseReleased(MouseEvent e1) {
			drawing.setMiddlePoint(e1.getX() - clicked[0], e1.getY()
					- clicked[1]);
			System.out.println("Neu X: " + e1.getX());
			drawing.repaint();
		}
	}
	/**
	 * Reagiert wenn das MouseWheel gedreht wird. Zoomed komplett (Sowohl x- als auch y-Achse) heran oder heraus
	 * 
	 * @author f.baumeister
	 *
	 */
	static class ListenerMouseWheel implements MouseWheelListener{
		private JSlider sliderX;
		private JSlider sliderY;
		
		public ListenerMouseWheel(JSlider sliderX, JSlider sliderY){
			this.sliderX = sliderX;
			this.sliderY = sliderY;
		}
		@Override
		public void mouseWheelMoved(MouseWheelEvent e) {
			sliderX.setValue(sliderX.getValue()+e.getWheelRotation());
			sliderY.setValue(sliderY.getValue()+e.getWheelRotation());
			
		}

		
		
	}
	/**
	 * Listener für den JSlider, der fuer die X-Achse zustaendig ist
	 * Wird er geschoben, werden die globalen Parameter bzgl. der x-Achse neu gesetzt 
	 * @author f.baumeister
	 *
	 */
	static class SliderXChangeListener implements ChangeListener{
		private PaintingComponent drawingArea;
		/**
		 * Konstruktor
		 * @param drawingArea Benoetigt eine Instanz von PaintingComponent, um auf deren Inhalte zugreifen zu können um Parameter zu setzen, die für den zoom zuständig sind
		 */
		public SliderXChangeListener(PaintingComponent drawingArea){
			this.drawingArea=drawingArea;
		}
		@Override
		public void stateChanged(ChangeEvent e) {
				JSlider source =(JSlider) e.getSource();
				if((int)source.getValue() > 0  &&  (int)source.getValue() < 100){
					this.drawingArea.setEinheitProPixelX(1.0*source.getValue()/(EINHEIT_MAX-source.getValue()));
					this.drawingArea.repaint();
				}
		}
		
	}
	
	/**
	 * Listener für den JSlider, der fuer die Y-Achse zustaendig ist
	 * Wird er geschoben, werden die globalen Parameter bzgl. der x-Achse neu gesetzt 
	 * @author f.baumeister
	 *
	 */
	static class SliderYChangeListener implements ChangeListener{
		private PaintingComponent drawingArea;
		/**
		 * Konstruktor
		 * @param drawingArea Benoetigt eine Instanz von PaintingComponent, um auf deren Inhalte zugreifen zu können um Parameter zu setzen, die für den zoom zuständig sind
		 */
		public SliderYChangeListener(PaintingComponent drawingArea){
			this.drawingArea=drawingArea;
		}
		@Override
		public void stateChanged(ChangeEvent e) {
				JSlider source =(JSlider) e.getSource();
				if((int)source.getValue() > 0  &&  (int)source.getValue() < 100){
					drawingArea.setEinheitProPixelY(1.0*source.getValue()/(EINHEIT_MAX-source.getValue()));
					drawingArea.repaint();
				}
				
				
		}
		
	}

	/**
	 * Diese Methode wird aufgerufen, sobald einer der Buttons gedrueckt wird.
	 * Jeder Button hat einen eigenen AktionCommand anhand dessen die Methode erkennt welcher Button gedrueckt wurde und eine dementsprechende Methode ausgeben kann.
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		if ("closeBtClicked".equals(e.getActionCommand())) {
			System.exit(0);
		}
		
		else if(e.getActionCommand().equals("showKFBtClicked")){
			String getKF = eingKFunktion.getText();
			try{
				drawingArea.setKostenF( new Funktion(getKF));
			}
			catch(Exception e1){
				JOptionPane.showMessageDialog(null, "Dies ist keine gueltige Funktionseingabe","Keine Funktion!",JOptionPane.ERROR_MESSAGE);
			}
			drawingArea.repaint();
			
		}
		else if(e.getActionCommand().equals("showEFBtClicked")){
			String getEF = eingEFunktion.getText();
			try{
				 drawingArea.setErloesF( new Funktion(getEF));
			}
			catch(Exception e1){
				JOptionPane.showMessageDialog(null, "Dies ist keine gueltige Funktionseingabe","Keine Funktion!",JOptionPane.ERROR_MESSAGE);
			}
			drawingArea.repaint();
			
		}
		else if(e.getActionCommand().equals("setMarkierungBtDeakt")){
			if(this.drawingArea.isErloesFunktion() || this.drawingArea.isKostenFunktion()){
				setMarkierungBt.setActionCommand("setMarkierungBtAkt");
				this.drawingArea.toogleMarkierung();
				this.drawingArea.repaint();
				setMarkierungBt.setIcon(markOn);
			}
			else{
				JOptionPane.showMessageDialog(null, "Um alle moeglichen Markierungen anzuzeigen muessen sowohl die Kosten-, als auch die Erloesfunktion gesetzt sein.","Nicht genuegend Argumente!",JOptionPane.ERROR_MESSAGE);
			}
		}
		else if(e.getActionCommand().equals("setMarkierungBtAkt")){
			setMarkierungBt.setActionCommand("setMarkierungBtDeakt");
			setMarkierungBt.setIcon(markOff);
			this.drawingArea.toogleMarkierung();
			this.drawingArea.repaint();
		}
		else if(e.getActionCommand().equals("aktFunktionUmschalten")){
			drawingArea.toogleAktFunktionen();
			drawingArea.repaint();
		}
		else if(e.getActionCommand().equals("setDff")){
			if(drawingArea.areBothFunctionsSet()){
				System.out.println("out");
				drawingArea.toogleDifference();
				drawingArea.repaint();
			}
			else{
					JOptionPane.showMessageDialog(null, "Um eine Differenzfunktion anzuzeigen muessen sowohl die Kosten-, als auch die Erloesfunktion gesetzt sein.","Nicht genuegend Argumente!",JOptionPane.ERROR_MESSAGE);
			}
		}
	}
	
	
}
